package com.sweet.imserver.db;

import com.sweet.imserver.MainVerticle;
import com.sweet.imserver.model.User;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Context;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mapdb.*;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;

public class DBVerticle extends AbstractVerticle {

  public static final String MESSAGE_ADDRESS = "message";
  public static final String USER_ADD = "add";
  public static final String USER_REMOVE = "remove";
  public static final String USER_SEARCH = "search";

  public static final String USER_EVENT_BUS = "user";
  public static final String MESSAGE_EVENT_BUS = "message";
  public static final String LOGIN_EVENT_BUS = "login";
  public static final String MESSAGE_QUERY_EVENT_BUS = "query_message";
  public static final String QUERY_OFFLINE_MESSAGE_EVENT_BUS = "query_offline_message";

  private static final String DB_PATH = "db" + File.separator;
  private DB userDb;
  private DB messageDb;

  // userId=obj
  private BTreeMap<String, String> userMap;
  // userId-friendId=note
  private HTreeMap<String, String> userNoteMap;
  // phone=pass_uid
  private HTreeMap<String, String> phonePasswordMap;
  // user-friendId=message array
  private HTreeMap<String, String> messageOfflineMap;

  private IndexTreeList<String> messageQueue;

  private EventBus eventBus;

  private final Logger logger = LogManager.getLogger(DBVerticle.class);

  @Override
  public void start(Promise<Void> startPromise) throws Exception {
    eventBus = vertx.eventBus();

    eventBus.<JsonObject>consumer(USER_EVENT_BUS, this::userHandler);

    eventBus.<JsonObject>consumer(USER_SEARCH, this::searchUserHandler);

    // 存储不在线用户消息
    eventBus.<JsonObject>consumer(MESSAGE_EVENT_BUS, this::messageHandler);
    // 查询离线消息
    eventBus.<JsonObject>consumer(MESSAGE_QUERY_EVENT_BUS, this::queryMessageHandler);

    eventBus.<JsonObject>consumer(LOGIN_EVENT_BUS, this::loginHandler);

    // 查询离线消息 New
    eventBus.<JsonObject>consumer(QUERY_OFFLINE_MESSAGE_EVENT_BUS, this::queryOfflineMessage);

    vertx.<Boolean>executeBlocking(e -> {
      initMap();
      e.complete(true);
    }, p -> {
      if (p.succeeded()) {
        insertTestUser();
        System.out.println("db verticle ok");
        startPromise.complete();
      } else {
        startPromise.fail(p.cause());
      }
    });
  }

  private void searchUserHandler(Message<JsonObject> jsonObjectMessage) {
    logger.debug("searchUser: ");
    JsonObject body = jsonObjectMessage.body();
    logger.debug("searchUser: " + body);
    String phone = body.getString("phone");
    String userPassword = phonePasswordMap.get(phone);
    if (userPassword == null) {
      jsonObjectMessage.reply(new JsonObject());
    } else {
      String[] s = userPassword.split("_");
      JsonObject userJson = new JsonObject(userMap.getOrDefault(s[1], "{}"));
      userJson.remove("password");
      userJson.remove("note");
      userJson.remove("friendList");
      jsonObjectMessage.reply(userJson);
    }
  }

  private void queryMessageHandler(Message<JsonObject> body) {
    JsonObject msg = body.body();
    String id = msg.getString("id");
    JsonObject resp = queryOfflineMessage(id, msg.getJsonArray("friendList"));
    body.reply(resp);
  }


  private void initMap() {
    createDbDir();
    userDb = DBMaker.fileDB(DB_PATH + "user.db").transactionEnable().closeOnJvmShutdown()
      .fileMmapEnable().make();
    messageDb = DBMaker.fileDB(DB_PATH + "message.db").transactionEnable().closeOnJvmShutdown()
      .fileMmapEnable().make();

    userMap = userDb.treeMap("userMap", Serializer.STRING, Serializer.STRING)
      .createOrOpen();
    userNoteMap = userDb.hashMap("userNoteMap", Serializer.STRING, Serializer.STRING)
      .createOrOpen();
    phonePasswordMap = userDb.hashMap("phonePassword", Serializer.STRING, Serializer.STRING)
      .createOrOpen();
    messageOfflineMap = messageDb.hashMap("messageOfflineMap", Serializer.STRING, Serializer.STRING)
      .createOrOpen();
    messageQueue = messageDb.indexTreeList("messageQueue", Serializer.STRING)
      .createOrOpen();
  }

  private void createDbDir() {
    Path path = Paths.get(DB_PATH);
    if (Files.notExists(path)) {
      try {
        Files.createDirectory(path);
      } catch (IOException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
      }
    }
  }

  private void loginHandler(Message<JsonObject> message) {
    JsonObject resp = new JsonObject();
    JsonObject body = message.body();
    String passwordAndUid = phonePasswordMap.get(body.getString("phone"));
    if (passwordAndUid == null) {
      resp.put("error", "该手机号尚未注册");
      message.reply(resp);
      return;
    }

    String[] s = passwordAndUid.split("_");
    if (!s[0].equals(body.getString("password"))) {
      resp.put("error", "用户名密码错误");
      message.reply(resp);
      return;
    }
    JsonObject userJson = new JsonObject(userMap.get(s[1]));
    JsonArray friendList = userJson.getJsonArray("friendList");
    JsonArray friendJsonArray = findFriendJson(userJson.getString("id"), friendList);
    JsonObject offlineMessage = queryOfflineMessage(userJson.getString("id"), friendList);
    System.out.println("offlineMessage: " + offlineMessage.encodePrettily());
    userJson.put("friendList", friendJsonArray);
    userJson.put("offlineMessage", offlineMessage);
    message.reply(userJson);
  }

  private void queryOfflineMessage(Message<JsonObject> message) {
    String userId = message.body().getString("id");
    JsonObject userJson = new JsonObject(userMap.get(userId));
    JsonObject offlineMessage = queryOfflineMessage(userJson.getString("id"),
      userJson.getJsonArray("friendList"));
    message.reply(offlineMessage);
  }

  private JsonArray findFriendJson(String uid, JsonArray friendList) {
    JsonArray friendJsonArray = new JsonArray();
    for (Object fId : friendList) {
      JsonObject friendJson = new JsonObject(userMap.get(fId.toString()));
      friendJson.remove("phone");
      friendJson.remove("password");
      friendJson.remove("friendList");
      String note = userNoteMap.getOrDefault(uid + "-" + friendJson.getString("id"), "");
      friendJson.put("note", note);
      friendJsonArray.add(friendJson);
    }
    return friendJsonArray;
  }

  private void insertTestUser() {
    if (userMap.isEmpty()) {
      String user0Id = UUID.randomUUID().toString().replaceAll("-", "");
      String user1Id = UUID.randomUUID().toString().replaceAll("-", "");
      String user2Id = UUID.randomUUID().toString().replaceAll("-", "");
      String user3Id = UUID.randomUUID().toString().replaceAll("-", "");
      User user = new User("", "18866259537", "王学鹏", "w123456789", "", "青岛",
        new JsonArray().add(user0Id).add(user1Id).add(user2Id).add(user3Id));
      userMap.put(user.getId(), user.toJson());

      User user0 = new User(user0Id, "", "18866259538", "李云龙", "w123456789", "李", "青岛",
        new JsonArray().add(user.getId()));
      userMap.put(user0Id, user0.toJson());

      User user1 = new User(user1Id, "", "18866259539", "二营长", "w123456789", "SweetNote", "北京",
        new JsonArray().add(user.getId()));
      userMap.put(user1Id, user1.toJson());

      User user2 = new User(user2Id, "", "18866259540", "一营长", "w123456789", "SweetNote", "北京",
        new JsonArray().add(user.getId()));
      userMap.put(user2Id, user2.toJson());

      User user3 = new User(user3Id, "", "18866259541", "赵刚", "w123456789", "SweetNote", "北京",
        new JsonArray().add(user.getId()));
      userMap.put(user3Id, user3.toJson());

      phonePasswordMap.put(user.getPhone(), user.getPassword() + "_" + user.getId());
      phonePasswordMap.put(user0.getPhone(), user0.getPassword() + "_" + user0.getId());
      phonePasswordMap.put(user1.getPhone(), user1.getPassword() + "_" + user1.getId());
      phonePasswordMap.put(user2.getPhone(), user2.getPassword() + "_" + user2.getId());
      phonePasswordMap.put(user3.getPhone(), user3.getPassword() + "_" + user3.getId());
      commit(userDb);
    }
  }

  private void messageHandler(Message<JsonObject> message) {
    JsonObject obj = message.body();
    String key = messageOfflineKey(obj.getString("toId"), obj.getString("fromId"));
    JsonArray messageArray = new JsonArray(messageOfflineMap.getOrDefault(key, "[]"));
    messageArray.add(obj);

    messageOfflineMap.put(key, messageArray.encode());
    commit(messageDb);
  }

  private void commit(DB db) {
    db.commit();
  }

  private void userHandler(Message<JsonObject> message) {
    JsonObject body = message.body();
    switch (body.getString("type")) {
      case USER_ADD:
      case USER_REMOVE:
      case USER_SEARCH:
    }
  }

  private JsonObject queryOfflineMessage(String id, JsonArray friendList) {
    JsonObject resp = new JsonObject();
    for (Object fid : friendList) {
      String f = fid.toString();
      String removeStr = messageOfflineMap.remove(messageOfflineKey(id, f));
      if (removeStr != null) {
        resp.put(f, new JsonArray(removeStr));
      }
    }
    commit(messageDb);
    return resp;
  }

  private String messageOfflineKey(String id, String fid) {
    return id + "-" + fid;
  }

  @Override
  public void stop(Promise<Void> stopPromise) throws Exception {
  }
}
